
package com.msteam.kernel.util;

import com.jfinal.kit.JsonKit;
import com.jfinal.kit.PathKit;
import com.jfinal.log.Log;

import java.io.*;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassUtils {

	private static final Log log = Log.getLog(ClassUtils.class);

	private static final String webClassPathFile = ".#webclasspath";  
	
	public static <T> List<Class<T>> scanSubClass(Class<T> pclazz) {
		return scanSubClass(pclazz, false);
	}

	 protected static List loadWebClassPathFile() {  
	        File cpFile = new File(PathKit.getWebRootPath(), webClassPathFile);  
	        if (cpFile.exists()) {  
	            FileReader reader = null;  
	            try {  
	                List rc = new ArrayList();  
	                reader = new FileReader(cpFile);  
	                LineNumberReader lr = new LineNumberReader(reader);  
	                String line = null;  
	                while ((line = lr.readLine()) != null) {  
	                    line = line.replace('\\', '/');  
	                    rc.add(line);  
	                }  
	                return rc;  
	            } catch (IOException ioEx) {  
	                if (reader != null)  
	                    ;  
	                return new ArrayList<String>();  
	            }  
	        }  
	        return  new ArrayList<String>();  
	    }  
	 
	public static <T> List<Class<T>> scanSubClass(Class<T> pclazz, boolean mustbeCanNewInstance) {
		if (pclazz == null) {
			log.error("scanClass: parent clazz is null");
			return null;
		}

		
		
		//List<String> webClassPath = loadWebClassPathFile();
		List<String> webClassPath = new ArrayList<>();

		List<Class<T>> classList = new ArrayList<Class<T>>();
		
		if(webClassPath.size()==0){
			webClassPath.add(PathKit.getRootClassPath());
		}
		System.out.println("扫描路径:"+JsonKit.toJson(webClassPath));

		for(String wcp : webClassPath){
			if(wcp.endsWith(".jar")){
				continue;
			}
			List<File> classFileList = new ArrayList<File>();
			scanClass(classFileList, wcp);

			for (File file : classFileList) {
	
				int start = wcp.length();
				int end = file.toString().length() - 6; // 6 == ".class".length();
	
				String classFile = file.toString().substring(start + 1, end);
				Class<T> clazz = classForName(classFile.replace(File.separator, "."));
	
				if (clazz != null && pclazz.isAssignableFrom(clazz)) {
					if (mustbeCanNewInstance) {
						if (clazz.isInterface())
							continue;
	
						if (Modifier.isAbstract(clazz.getModifiers()))
							continue;
					}
					classList.add(clazz);
				}
			}
		}
		File jarsDir = new File(PathKit.getWebRootPath() + "/WEB-INF/lib");
		if (jarsDir.exists() && jarsDir.isDirectory()) {
			File[] jarFiles = jarsDir.listFiles(new FileFilter() {
				@Override
				public boolean accept(File pathname) {
					String name = pathname.getName().toLowerCase();
					return name.endsWith(".jar") && name.startsWith("jff");
				}
			});

			if (jarFiles != null && jarFiles.length > 0) {
				for (File f : jarFiles) {
					classList.addAll(scanSubClass(pclazz, f, mustbeCanNewInstance));
				}
			}
		}

		return classList;
	}

	public static <T> List<Class<T>> scanSubClass(Class<T> pclazz, File f, boolean mustbeCanNewInstance) {
		if (pclazz == null) {
			log.error("scanClass: parent clazz is null");
			return null;
		}

		JarFile jarFile = null;

		try {
			jarFile = new JarFile(f);
			List<Class<T>> classList = new ArrayList<Class<T>>();
			Enumeration<JarEntry> entries = jarFile.entries();

			while (entries.hasMoreElements()) {
				JarEntry jarEntry = entries.nextElement();
				String entryName = jarEntry.getName();
				if (!jarEntry.isDirectory() && entryName.endsWith(".class")) {
					String className = entryName.replace("/", ".").substring(0, entryName.length() - 6);
					Class<T> clazz = classForName(className);
					if (clazz != null && pclazz.isAssignableFrom(clazz)) {
						if (mustbeCanNewInstance) {
							if (clazz.isInterface())
								continue;

							if (Modifier.isAbstract(clazz.getModifiers()))
								continue;
						}
						classList.add(clazz);
					}
				}
			}

			return classList;

		} catch (IOException e1) {
		} finally {
			if (jarFile != null)
				try {
					jarFile.close();
				} catch (IOException e) {
				}
		}

		return null;

	}

	@SuppressWarnings("unchecked")
	private static <T> Class<T> classForName(String className) {
		Class<T> clazz = null;
		try {
			ClassLoader cl = Thread.currentThread().getContextClassLoader();
			clazz = (Class<T>) Class.forName(className, false, cl);
		} catch (Throwable e) {
			log.error("classForName is error，className:" + className);
		}
		return clazz;
	}

	private static void scanClass(List<File> fileList, String path) {
		File files[] = new File(path).listFiles();
		if (null == files || files.length == 0)
			return;
		for (File file : files) {
			if (file.isDirectory()) {
				scanClass(fileList, file.getAbsolutePath());
			} else if (file.getName().endsWith(".class")) {
				fileList.add(file);
			}
		}
	}

}
